/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.rationalsheet.parser;

import java.util.ArrayList;
import java.util.Collection;
import java.util.TreeSet;
import org.rationalsheet.parser.operation.DefaultOperations;
import org.rationalsheet.parser.operation.Operation;
import org.rationalsheet.parser.type.DefaultTypes;
import org.rationalsheet.parser.type.Type;

/**
 *
 * @author Overmind
 */
public class Parser
{
    public static String WRONG_DATA = "#WRONG_DATA#";
    public static String DIVISION_BY_ZERO = "#NaN#";
 
    public static Parser DEFAULT_PARSER = null;
    
    private TreeSet<Operation> operations;
    private TreeSet<Type> types;

    public Parser()
    {
	operations = DefaultOperations.getOperations();
	types = DefaultTypes.getTypes();
    }
    
    public Parser(Collection<Operation> operations, Collection<Type> types)
    {
	this.operations = new TreeSet(operations);
        this.types = new TreeSet<>(types);
    }
    
    public static Parser getDefaultParser()
    {
        if(DEFAULT_PARSER == null)
        {
            DEFAULT_PARSER = new Parser();
        }
        
        return DEFAULT_PARSER;
    }
    
    public String parse(String data)
    {        
        
        for(Type type : types)
	{
	    try
	    {
		return type.objectToString(type.valueOfString(data));
	    }
	    catch(NumberFormatException nfe)
	    {
		continue;
	    }
	}
        
        boolean casted = false;
        
        for(Operation operation : operations)
        {
            ArrayList<String[]> possibleArguments =
                    operation.getExpression().getPossibleArguments(data);

            for (String[] argumentsNotParsed : possibleArguments)
            {
                String[] argumentsParsed = new String[argumentsNotParsed.length];

                for (int i = 0; i < argumentsNotParsed.length; ++i)
                {
                    argumentsParsed[i] = parse(argumentsNotParsed[i]);
                    
                    if(argumentsParsed[i].equals(WRONG_DATA) |
                            argumentsParsed[i].equals(DIVISION_BY_ZERO) |
                            argumentsParsed[i].equals(Type.CAST_ERROR))
                    {
                        return argumentsParsed[i];
                    }
                }

                try
                {
                    return operation.calculate(argumentsParsed);
                } 
                catch (NumberFormatException nfe)
                {
                    casted = true;
                    continue;
                }
                catch(ArithmeticException ae)
                {
                    if(ae.getMessage().equals("Division by zero"))
                    {
                        return DIVISION_BY_ZERO;
                    }
                    else
                    {
                        throw ae;
                    }
                }
            }
        }
        
        if(casted)
        {
            return Type.CAST_ERROR;
        }
        
	return WRONG_DATA;
    }
}
